package com.youpai.platform.jarvis.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.youpai.platform.jarvis.enums.ProcessCodeEnum;
import com.youpai.platform.jarvis.exceptions.BaseBizException;
import com.youpai.platform.jarvis.mapper.LogEventMapper;
import com.youpai.platform.jarvis.mapper.LogEventPO;
import com.youpai.platform.jarvis.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;

/**
 * @date: 2021/11/28 23:06
 * @author: Anna
 * @version: 1.0
 * @description: 文件service实现
 */
@Slf4j
@Service
public class FileServiceImpl implements FileService {

    private ExecutorService executorService;

    private String preFilePath = "";

    private Integer prePid = null;
    @Resource
    private LogEventMapper logEventMapper;

    @Override
    public void uploadFile(MultipartFile file) {
        String realFileName = file.getOriginalFilename();
        if (StringUtils.isBlank(realFileName)) {
            log.error("上传文件文件名为空");
            throw new BaseBizException(ProcessCodeEnum.UP_LOAD_FILE_NAME_CAN_NOT_BE_NULL);
        }
        String path = System.currentTimeMillis() + ".jar";
        // 创建一个临时文件
        File temporaryFile = new File(path);
        try {
            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(temporaryFile));
            out.write(file.getBytes());
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            log.error(ProcessCodeEnum.FILE_NOT_EXIST.getDesc());
            throw new BaseBizException(ProcessCodeEnum.FILE_NOT_EXIST);
        } catch (IOException e) {
            throw new BaseBizException(ProcessCodeEnum.PARAM_ERROR);
        }
        killPreJar();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        startThisJar(temporaryFile);

    }

    private void refreshThreadPool() {
        if (null != executorService) {
            executorService.shutdownNow();
            executorService = null;
        }
        ThreadFactory threadFactory = new CustomizableThreadFactory("jarvis-");
        executorService = new ThreadPoolExecutor(10, 20, 2000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1000), threadFactory);
    }

    private void startThisJar(File temporaryFile) {
        preFilePath = temporaryFile.getPath();
        String command = "java -jar -Xms2048m -Xmx2048m " + preFilePath + "1 > " + temporaryFile.getName() + "-stdout.log 2 > " + temporaryFile.getName() + "-stderr.log";
        Process exec = null;
        String[] params = new String[]{"Xms=2048m", "Xmx=2048m"};
        try {
            exec = Runtime.getRuntime().exec(command, params, temporaryFile.getParentFile());
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        Field pidField = null;
        try {
            pidField = exec.getClass().getDeclaredField("pid");
            pidField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        try {
            if (null == pidField) {
                return;
            }
            prePid = pidField.getInt(exec);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        log.info("exec {}", JSONObject.toJSONString(exec));
        final Process finalExec = exec;
        final BufferedReader[] errorReader = {new BufferedReader(new InputStreamReader(finalExec.getErrorStream(), StandardCharsets.UTF_8))};
        final BufferedReader[] inputReader = {new BufferedReader(new InputStreamReader(finalExec.getInputStream(), StandardCharsets.UTF_8))};
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.execute(() -> {
            String line;
            while (true) {
                try {
                    if (StringUtils.isNotBlank(line = errorReader[0].readLine())) {
                        String finalLine = line;
                        executorService.execute(() -> doInsertLog(1, finalLine));
                    }
                } catch (Exception exception) {
                    log.error("err : {}", exception.getMessage());
                }
            }
        });
        executorService.execute(() -> {
            String line;
            while (true) {
                try {
                    if (StringUtils.isNotBlank(line = inputReader[0].readLine())) {
                        String finalLine = line;
                        executorService.execute(() -> doInsertLog(0, finalLine));
                    }
                } catch (Exception exception) {
                    log.error("err : {}", exception.getMessage());
                }
            }
        });

    }

    private void killPreJar() {
        if (null == prePid || 0 == prePid) {
            refreshThreadPool();
            return;
        }
        String command = "kill -9 " + prePid;
        Process exec = null;
        try {
            exec = Runtime.getRuntime().exec(command);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            refreshThreadPool();
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("exec {}", JSONObject.toJSONString(exec));
    }

    private void doInsertLog(Integer level, String msg) {
        final LogEventPO logEventPO = new LogEventPO()
                .setLog(msg)
                .setAppId(1)
                .setLevel(level);
        logEventMapper.insert(logEventPO);
    }
}
